class DeliveryProcess
  include ActiveModel::Model

  attr_reader :delivery, :queue_name, :domain_name, :pid_file

  def initialize(delivery)
    @delivery = delivery
    @pid_file = "tmp/pids/#{delivery.promotion_mail_id}-#{delivery.domain_name}.pid"
    @queue_name = "mail:#{delivery.promotion_mail_id}:#{delivery.domain_name}"
    @domain_name = delivery.domain_name
    @hang = false
    @redis_main = Redis.new(REDIS_CONF)
    @redis_monitor = Redis.new(REDIS_CONF)
  end

  def start
    pid = fork do
      File.open(@pid_file, 'w'){|f| f.write Process.pid}

      @redis_main.client.reconnect
      @redis_monitor.client.reconnect

      @delivery.update started_at: Time.current,
                       status: DeliveryStatus::SENDING,
                       total: @redis_main.llen(queue_name)

      redis_subscribe

      until (recipient_ids = (0...BATCH_SIZE).to_a.map{@redis_main.lpop queue_name}.compact).empty?
        exit_when_pause
        while @hang
          @hang = false
          exit_when_pause
          sleep HANG_INTERVAL
        end
        exit_when_pause
        yield recipient_ids
        sleep DELIVERY_INTERVAL
      end

      @delivery.update completed_at: Time.current,
             status: DeliveryStatus::COMPLETE,
             delivered: @delivery.total

      File.delete @pid_file
    end
    Process.detach pid
  end
  alias resume start

  %i(pause stop).each do |action|
    define_method action do                      # def pause
      @redis_main.publish action, queue_name     #   @redis_main.publish :pause, queue_name
    end                                          # end
  end

  def hang
    @redis_main.publish :hang, domain_name
  end

  private
  def exit_when_pause
    if @pause
      File.delete pid_file
      exit
    end
  end

  def redis_subscribe
    Thread.new do
      @redis_monitor.subscribe :pause, :stop, :hang do |on|
        on.message do |channel, message|
          send :"perform_#{channel}", message
        end
      end
    end
  end

  def perform_pause(message)
    return unless message == queue_name
    @pause = true
    @delivery.update(status: DeliveryStatus::PAUSE)
  end

  def perform_resume(message)
    return unless message == queue_name
    @pause = false
    @delivery.update(status: DeliveryStatus::SENDING)
  end

  def perform_stop(message)
    return unless message == queue_name
    @redis_main.del queue_name
  end

  def perform_hang(message)
    return unless message == domain_name
    @hang = true
  end
end